
/*
 * Here is the implementation of all the functions used to implement the parallel
 * boot method.
 * To use it valid dependenciesTable and deltaTable must be supplied 
 * 
 * It is possible to switch from one mode to another by setting the boot_mode
 * variable to PARALLEL or REGULAR in bootTables.h.
 */


int __init isSonOf(int index, int i) {
	int retour = -1;
	int j;
	for (j = 0 ; j < GRAPHDEPTH ; j++) {
		if (dependenciesList[i][j] == index) {
			retour = j;
			break;	
		} 
	} 
	return retour;
}

int lock;

static void __init loadingThread(void) {

	unsigned long start, stop, delta,a ,b,c;
	start = gbus_read_reg32(REG_BASE_system_block + SYS_xtal_in_cnt);
	int i ;
	for ( i=0 ; i<= 10000000 ; i++ )
	{
		a = gbus_read_reg32(REG_BASE_system_block + SYS_xtal_in_cnt);
		b = gbus_read_reg32(REG_BASE_system_block + SYS_xtal_in_cnt)*5;
		c = a * b %i;
	}	
	stop = gbus_read_reg32(REG_BASE_system_block + SYS_xtal_in_cnt);
	delta = (stop > start) ? (stop - start) : (0xffffffff - start + stop + 1);
	printk("!*!$$!*!Delta%lu\n",delta);	
	lock--;
}

static void __init callThread(int index) {
	initcall_t *call;
	call = __initcall_start + (int)index;
	(*call)();
	lock--;	
}

void __init initThread(int index) {
	set_current_state(TASK_INTERRUPTIBLE);	
	initcall_t *call;
	call = __initcall_start + (int)index;	
	if (assess_initcalls) {
		lock = 2;	
		kernel_thread(loadingThread,NULL,0);
		kernel_thread(callThread,index,0);		
		while(lock!= 0) {
			yield();
		}	
	}
	else {
		int ret = (*call)();		
		if (ret != 0 )
			printk("!*!$$!*!Error!*!$$!*!"); 
		//printk("After %d\n",index);
	}
	load -= deltaList[index][1];
	/*Update the list*/
	int i;
	for ( i = 0 ; i < NBR_OF_INITCALLS ; i++){
		if (isSonOf(index,i) != mode) dependenciesList[i][isSonOf(index,i)] = mode; 
	}

	i = NBR_OF_INITCALLS-1;
	while( (load < (long)LIMIT) && ( i>=0 ) ){
		if ((isOrphan(deltaList[i][0])) && ((deltaList[i][1] +load) < LIMIT) ) {
			load += deltaList[i][1];	
			int j;
			for (j= 0 ; j< GRAPHDEPTH ; j++) {
				dependenciesList[deltaList[i][0]][j] = -2;
			}	
			launchScript(deltaList[i][0]);
		}
		i--;
	}
	atomic_dec( &goOn );	 
}	

int __init isOrphan(int index) {

	int retour = 1;
	int i;
	for (i = 0 ; i< GRAPHDEPTH ; i++) {
		if (dependenciesList[index][i] != mode) {
			retour = 0;
			break;		
		}
	}

	return retour;
}

void __init launchScript(int index) {
	//Launch a thread
	atomic_inc(&goOn);
	pid_t pid = kernel_thread(initThread,index, CLONE_FS | CLONE_SIGHAND);
	pidList[positionInPidList] = pid ;
	positionInPidList ++;
}

void __init do_parallel_boot(void) {
	
	if (assess_initcalls) {printk("++++++++"); loadingThread();}
	
	ktime_t t0, t1, delta;
	t0 = ktime_get();
	atomic_set( &goOn, 0 );
	load = 0;
	mode = -1;
	int i = NBR_OF_INITCALLS -1;
	while( (load < (long)LIMIT) && ( i>=0 ) ){
		if ((isOrphan(deltaList[i][0])) && ((deltaList[i][1] +load) < LIMIT) ) {
			load += deltaList[i][1];	
			int j;
			for (j= 0 ; j< GRAPHDEPTH ; j++) {
				dependenciesList[deltaList[i][0]][j] = -2;
			}	
			launchScript(deltaList[i][0]);
		}
		i--;
	}
	do { yield(); } while ( atomic_read( &goOn ) != 0); 
	flush_scheduled_work();
	t1 = ktime_get();	
	delta = ktime_sub(t1, t0);
	printk("!*!$$!*BootTime%Ld\n",(unsigned long long)delta.tv64 >> 20);
	
	//Then make sure that all the threads do not exist anymore by killing them
	for ( i = 0 ; i < NBR_OF_INITCALLS ; i++ ) {
			if (pidList[i] != 0)
				(void)kill_proc(pidList[i], SIGTERM, 1);

	}

}

void __init do_regular_boot() {
	
	  initcall_t *call;
         int count = preempt_count();
 		 printk("!*!$$!*NbrOfInitCalls%d\n",__initcall_end - __initcall_start);
         for (call = __initcall_start; call < __initcall_end; call++) {
                 ktime_t t0, t1, delta;
                 char *msg = NULL;
                 char msgbuf[40];
                 int result;
 
                 if (initcall_debug) {
                         printk("Calling initcall 0x%p", *call);
                         print_fn_descriptor_symbol(": %s()",
                                         (unsigned long) *call);
                         printk("\n");
                         t0 = ktime_get();
                 }
 
                 result = (*call)();
 
                 if (initcall_debug) {
                         t1 = ktime_get();
                         delta = ktime_sub(t1, t0);
 
                         printk("initcall 0x%p", *call);
                         print_fn_descriptor_symbol(": %s()",
                                         (unsigned long) *call);
                         printk(" returned %d.\n", result);
 
                         printk("initcall 0x%p ran for %Ld msecs: ",
                                 *call, (unsigned long long)delta.tv64 >> 20);
                         print_fn_descriptor_symbol("%s()\n",
                                 (unsigned long) *call);
                 }
 
                 if (result && result != -ENODEV && initcall_debug) {
                         sprintf(msgbuf, "error code %d", result);
                         msg = msgbuf;
                 }
                 if (preempt_count() != count) {
                         msg = "preemption imbalance";
                         preempt_count() = count;
                 }
                 if (irqs_disabled()) {
                         msg = "disabled interrupts";
                         local_irq_enable();
                 }
                 if (msg) {
                        printk(KERN_WARNING "initcall at 0x%p", *call);
                         print_fn_descriptor_symbol(": %s()",
                                         (unsigned long) *call);
                         printk(": returned with %s\n", msg);
                 }
         }
 
         /* Make sure there is no pending stuff from the initcall sequence */
         flush_scheduled_work();
	
}

void software_watchdog(void) {/*Not really a watchdog, just something to reboot automatically after 14 s*/	
    ssleep(15);	
	emergency_restart();
}

void __init free_initmemThread(void) {
	set_current_state(TASK_INTERRUPTIBLE);
	while (atomic_read( &goOn ) != 0 ) {
		yield();
	}
	free_initmem();
}

void __init correctDependenciesList(void) {
		
		int i,j;
		for (i = 0 ; i < NBR_OF_INITCALLS ; i++ )
			for ( j=0 ; j < GRAPHDEPTH ; j++ )
				if ( dependenciesList[i][j] == -1 ) dependenciesList[i][j] = -3 ;
}

void __init doDefferedInitcalls(void) {
	set_current_state(TASK_INTERRUPTIBLE);
	yield();
	mode = -3; //Do the rest of the initcalls...
	correctDependenciesList();
	load = 0;
	int i = NBR_OF_INITCALLS -1;
	while( (load < (long)LIMIT) && ( i>=0 ) ){
		if ((isOrphan(deltaList[i][0])) && ((deltaList[i][1] +load) < LIMIT) ) {
			load += deltaList[i][1];	
			int j;
			for (j= 0 ; j< GRAPHDEPTH ; j++) {
				dependenciesList[deltaList[i][0]][j] = -2;
			}	
			launchScript(deltaList[i][0]);
		}
		i--;
	}
	do { yield(); } while ( atomic_read(&goOn) != 0); 
	flush_scheduled_work();
	kernel_thread(free_initmemThread,NULL,CLONE_FS | CLONE_SIGHAND);
}
